using System;
using System.Configuration;
using System.Data;
using System.IO;
using System.Xml;

namespace FormsGenerator.DataAccess
{
	/// <summary>
	/// Class used for representing forms as files when we do not use a database.
	/// </summary>
	public class FormFiles
	{
		private const string cacheKey = "FormsDataTable";
		private const string GuidColumnName = "guid";
		private const string NameColumnName = "Name";
		private const string CreatedDateColumnName = "CreatedDate";
		private const string OwnerColumnName = "Owner";
		private const string XMLColumnName = "XML";
		private const string LockedByColumnName = "LockedBy";
		private const string LockedSinceColumnName = "LockedSince";
	
		/// <summary>
		/// Load all files into a databable. each row represents a form. this is analogous to a db implementation.
		/// </summary>
		/// <returns>the filled datatable</returns>
		public static DataTable Load()
		{
		    DataTable retVal = LoadDataTable();
			if(retVal == null || retVal.Rows.Count <= 0)
			{
				string path = GetPath();
				LoadDataTable();
				foreach(string fileName in Directory.GetFiles(path, "*.xml"))
				{
				    StreamReader sr = new StreamReader(fileName);
					string contents = sr.ReadToEnd();
					sr.Close();

				    CreateFormRow( fileName, contents);
				}
			}
			return retVal;
		}

		public static DataRow AddFormRow(Guid guid, string name, DateTime createdDate, string owner, string xml, string lockedBy, DateTime lockedSince)
		{
			DataTable allforms = LoadDataTable();
			DataRow row = allforms.NewRow();
			//just in case we get some xmlformatting that may potentially harm our "wrapping" xml, we remove the encoding tag:
			int firstOccurence = xml.IndexOf('?');
			if(firstOccurence > 0 && firstOccurence < 3)
			{
				int nextOccurence = xml.IndexOf('?',firstOccurence + 1);
			    int start = firstOccurence - 1;
				int count = nextOccurence + 1 - start;
				xml = xml.Remove(start, count + 1);
			}

			row[GuidColumnName] = guid;
			row[NameColumnName] = name;
			row[CreatedDateColumnName] = createdDate;
			row[OwnerColumnName] = owner;
			row[XMLColumnName] = xml;
			row[LockedByColumnName] = lockedBy;
			if(lockedSince.Equals(DateTime.MinValue)) row[LockedSinceColumnName] = DBNull.Value;
			else row[LockedSinceColumnName] = lockedSince;

			allforms.Rows.Add(row);
			return row;
		}

		public static void RemoveFormRow(Guid guid)
		{
			DataTable allforms = LoadDataTable();
			DataRow deleteRow = allforms.Rows.Find(guid);
			if(deleteRow  != null)
			{
				allforms.Rows.Remove(deleteRow);
			}
		}

		/// <summary>
		/// Saves a form to the disk. 
		/// </summary>
		public static bool SaveForm(Guid guid)
		{
			DataTable allforms = LoadDataTable();
			bool saveSuccess = false;
			
			string path = GetPath();
			string filename = path + guid + ".xml";

			try
			{
				DataRow row = allforms.Rows.Find(guid);
				
				if(row != null)
				{
					//first; rename the existing file (if any):
					if(File.Exists(filename))
					{
						File.Move(filename, filename + "OLD");
					}
					//then; create new file (read from the datatable)
					XmlDocument xmlDoc = new XmlDocument();
					XmlElement element = xmlDoc.CreateElement("formInfo");

					XmlAttribute attrib = xmlDoc.CreateAttribute("name");
					attrib.Value = row[NameColumnName].ToString();
					element.Attributes.Append(attrib);
					attrib = xmlDoc.CreateAttribute("createdDate");
					attrib.Value = row[CreatedDateColumnName].ToString();
					element.Attributes.Append(attrib);
					attrib = xmlDoc.CreateAttribute("owner");
					attrib.Value = row[OwnerColumnName].ToString();
					element.Attributes.Append(attrib);
					attrib = xmlDoc.CreateAttribute("lockedBy");
					attrib.Value = row[LockedByColumnName].ToString();
					element.Attributes.Append(attrib);
					attrib = xmlDoc.CreateAttribute("lockedSince");
					attrib.Value = row[LockedSinceColumnName].ToString();
					element.Attributes.Append(attrib);

					element.InnerXml = row[XMLColumnName].ToString();
					//create xml declaration:
					xmlDoc.AppendChild(element);
					XmlDeclaration xmldecl = xmlDoc.CreateXmlDeclaration("1.0","utf-8",null);

					//Add the new node to the document.
					XmlElement root = xmlDoc.DocumentElement;
					xmlDoc.InsertBefore(xmldecl, root);

					xmlDoc.Save(filename);
				}
				if(File.Exists(filename + "OLD"))
				{
					File.Delete(filename + "OLD");
				}
				saveSuccess = true;
			}
			catch
			{
				//if something fails, skip the save. 
				if(File.Exists(filename + "OLD"))
				{
					if(File.Exists(filename))
					{
						File.Delete(filename + "OLD");
					}
					else
					{
						File.Move(filename + "OLD", filename);
					}
				}
			}
			return saveSuccess;
		}

		public static void SaveAllForms()
		{
			string path = GetPath();
			DataTable allforms = LoadDataTable();
			//have to check if any files are deleted, must remove these from the folder
			foreach(string file in Directory.GetFiles(path, "*.xml"))
			{
				
				Guid guid = GetGuidFromFilename(file);
				
				DataRow row = allforms.Rows.Find(guid);
				
				if(row == null)
				{
					//then this form is deleted. remove it:
					File.Delete(path + guid + ".xml");
				}
			}
			foreach(DataRow row in allforms.Rows)
			{
				string guid = row[GuidColumnName].ToString();
			    Guid newGuid = new Guid(guid);
			    SaveForm(newGuid);
			}
		}

		public static DataTable CreateDataTableStructure()
		{
			DataTable retVal = new DataTable("FormsGenerator");
		    DataColumn column = retVal.Columns.Add(GuidColumnName, typeof(Guid));
			column.AllowDBNull = false;
			column = retVal.Columns.Add(NameColumnName, typeof(string));
			column.AllowDBNull = false;
			column = retVal.Columns.Add(CreatedDateColumnName, typeof(DateTime));
			column.AllowDBNull = false;
			column = retVal.Columns.Add(OwnerColumnName, typeof(string));
			column.AllowDBNull = false;
			column = retVal.Columns.Add(XMLColumnName, typeof(string));
			column.AllowDBNull = false;
			retVal.Columns.Add(LockedByColumnName, typeof(string));
			retVal.Columns.Add(LockedSinceColumnName, typeof(DateTime));
				
			DataColumn[] key = new DataColumn[1];
			key[0] = retVal.Columns[GuidColumnName];
			retVal.PrimaryKey = key;
			return retVal;
		}

		private static DataTable LoadDataTable()
		{
			object oDataTable = System.Web.HttpContext.Current.Cache.Get(cacheKey);
			DataTable retVal;
			if(oDataTable == null)
			{
				//create the datatable and the structure for the rows.
				retVal = CreateDataTableStructure();

				System.Web.HttpContext.Current.Cache.Insert(cacheKey, retVal);
			}
			else
			{
				retVal = (DataTable)oDataTable;
			}
			return retVal;
		}

		private static void CreateFormRow(string filename, string contents)
		{
		    Guid guid = GetGuidFromFilename(filename);
			XmlDocument xmlDoc = new XmlDocument();
			xmlDoc.LoadXml(contents);
			XmlElement element = xmlDoc.DocumentElement;

		    if (element != null)
		    {
		        XmlAttributeCollection attribs = element.Attributes;
		        string name = attribs["name"].Value;
		        DateTime createdDate = DateTime.Parse(attribs["createdDate"].Value);
		        string owner = attribs["owner"].Value;
		        string xml = element.InnerXml;
		        string lockedBy = attribs["lockedBy"].Value;
		        DateTime lockedSince = attribs["lockedSince"].Value != "" ? DateTime.Parse(attribs["lockedSince"].Value) : DateTime.MinValue;
			
			
		        AddFormRow(guid, name, createdDate, owner, xml, lockedBy, lockedSince);
		    }
		}

		private static Guid GetGuidFromFilename(string filename)
		{
			filename = filename.Replace(".xml", "");
			string[] fileNameParts = filename.Split('\\');
			string guid = fileNameParts[fileNameParts.Length - 1];
		    return new Guid(guid);
		}

		private static string GetPath()
		{
            string path = ConfigurationManager.AppSettings["FileRepository"];
			if(!path.EndsWith("\\")) path+= "\\";
			if(!Directory.Exists(path)) Directory.CreateDirectory(path);
			return path;
		}
	}

	public class FormSettings
	{
		private const string GuidColumnName = "guid";
		private const string FormsAccessIdColumnName = "FormsAccessId";
		private const string UserIdColumnName = "UserId";
		private const string cacheKey = "FormsAccess";
		private static int _highestAccessId;

		public static DataTable Load()
		{
			DataTable settings = LoadDataTable();
			if(settings == null || settings.Rows.Count <= 0)
			{
				string path = GetPath();
				foreach(string fileName in Directory.GetFiles(path, "*.config"))
				{
				    StreamReader sr = new StreamReader(fileName);
					string contents = sr.ReadToEnd();
					sr.Close();

				    CreateAccessRow(contents);
				}
			}
			return settings;
		}

		public static void AddAccessRow(Guid guid, string userId)
		{
			DataTable settings = LoadDataTable();
			DataRow row = settings.NewRow();
			_highestAccessId++;

			row[GuidColumnName] = guid;
			row[FormsAccessIdColumnName] = _highestAccessId;
			row[UserIdColumnName] = userId;
			
			settings.Rows.Add(row);
		}

		public static bool Save()
		{
			DataTable settings = LoadDataTable();
			bool saveSuccess = false;
			string path = GetPath();

			try
			{
				foreach(string fileName in Directory.GetFiles(path, "*.config"))
				{
					//rename existing files, later; delete them.
					File.Move(fileName, fileName + "OLD");
				}

				if(settings.Rows.Count > 0)
				{	
					//create new file (read from the datatable)
					XmlDocument xmlDoc = new XmlDocument();
					XmlElement element = xmlDoc.CreateElement("formSettings");
					foreach(DataRow row in settings.Rows)
					{
						XmlElement child = xmlDoc.CreateElement("setting");
						XmlAttribute attrib = xmlDoc.CreateAttribute("formsAccessId");
						attrib.Value = row[FormsAccessIdColumnName].ToString();
						child.Attributes.Append(attrib);
						attrib = xmlDoc.CreateAttribute("guid");
						attrib.Value = row[GuidColumnName].ToString();
						child.Attributes.Append(attrib);
						attrib = xmlDoc.CreateAttribute("userId");
						attrib.Value = row[UserIdColumnName].ToString();
						child.Attributes.Append(attrib);
						element.AppendChild(child);
					}
					xmlDoc.AppendChild(element);
					//create xml declaration:
					xmlDoc.AppendChild(element);
					XmlDeclaration xmldecl = xmlDoc.CreateXmlDeclaration("1.0","utf-8",null);

					//Add the new node to the document.
					XmlElement root = xmlDoc.DocumentElement;
					xmlDoc.InsertBefore(xmldecl, root);
					xmlDoc.Save(path + "formsAccess.config");
				}
				
				foreach(string fileName in Directory.GetFiles(path, "*.configOLD"))
				{
					File.Delete(fileName);
				}
				saveSuccess = true;
			}
			catch
			{
				//if something fails, skip the save. 
				foreach(string fileName in Directory.GetFiles(path, "*.configOLD"))
				{
					File.Move(fileName, fileName.Replace("configOLD", "config"));
				}
			}
			return saveSuccess;
		}

		public static DataTable CreateDataTableStructure()
		{
			DataTable retVal = new DataTable("FormsAccess");

		    DataColumn column = retVal.Columns.Add(FormsAccessIdColumnName, typeof(int));
			column.AllowDBNull = false;
			column =retVal.Columns.Add(GuidColumnName, typeof(Guid));
			column.AllowDBNull = false;
			column = retVal.Columns.Add(UserIdColumnName, typeof(string));
			column.AllowDBNull = false;
				
			DataColumn[] key = new DataColumn[1];
			key[0] = retVal.Columns[FormsAccessIdColumnName];
			retVal.PrimaryKey = key;

			return retVal;
		}


		public static void Remove(int formAccessId)
		{
			DataTable settings = LoadDataTable();
			DataRow deleteRow = settings.Rows.Find(formAccessId);
			if(deleteRow  != null)
			{
				settings.Rows.Remove(deleteRow);
			}
		}
		
		private static DataTable LoadDataTable()
		{
			DataTable retVal;
			object oDataTable = System.Web.HttpContext.Current.Cache.Get(cacheKey);
			if(oDataTable == null)
			{
				//create the datatable and the structure for the rows.
				retVal = CreateDataTableStructure();
				
				System.Web.HttpContext.Current.Cache.Insert(cacheKey, retVal);
			}
			else
			{
				retVal = (DataTable)oDataTable;
			}
			return retVal;
		}
		
		private static void CreateAccessRow(string contents)
		{
		    XmlDocument xmlDoc = new XmlDocument();
			xmlDoc.LoadXml(contents);
			
			XmlNode topNode = xmlDoc.SelectSingleNode("/formSettings");

		    foreach(XmlNode child in topNode)
			{
				XmlAttributeCollection attribs = child.Attributes;

				int formsAccessId = int.Parse(attribs["formsAccessId"].Value);
				Guid guid = new Guid(attribs["guid"].Value);
				string userId = attribs["userId"].Value;

				if(formsAccessId > _highestAccessId) _highestAccessId = formsAccessId;
				AddAccessRow(guid, userId);
			}
		}

		private static string GetPath()
		{
            string path = ConfigurationManager.AppSettings["FileRepository"];
			if(!path.EndsWith("\\")) path+= "\\";
			if(!Directory.Exists(path)) Directory.CreateDirectory(path);
			return path;
		}

		
	}
}
